#ifndef _MOUSE_H_
#define _MOUSE_H_

#include "math/vec2.h"
#include <vector>
namespace input
{
class MouseListener;

class Mouse
{
public:
	enum CONSTANTS
	{
		BUFFER_SIZE	=	256
	};
	enum BUTTONS
	{
		BUTTON_LEFT,
		BUTTON_RIGHT,
		BUTTON_CENTER,      
		BUTTON_COUNT
	};
	struct	BUTTON_STATE
	{
		BUTTONS				ButtonID;
		bool				ButtonChanged;	// did it change this frame
		bool				ButtonDown;		// is the key down ?
		float				ClickTime;		// last time it was clicked. Specify per button?
		float				RepeatInterval;

		// test the key
		inline bool			IsButtonPressed()  { return ButtonChanged && ButtonDown; };
		inline bool			IsButtonReleased() { return ButtonChanged && !ButtonDown; };

		// set up state
							BUTTON_STATE();
		void				SetState(bool Down, float TimeStamp);
	};

	static	Mouse&					Instance();


	bool							Tick(float dt);
	
	// update motions
	void							OnMouseMove(const math::Vec2& P);
	void							OnMousePress(BUTTONS b);
	void							OnMouseRelease(BUTTONS b);
	void							OnMouseWheel(int Dir);
	void							OnMouseEnter();
	void							OnMouseLeave();
    BUTTONS                         MapTouchToButton(long FingerID);



	/// add a listener
	void							AddListener(MouseListener* _pListener);
	void							DropListener(MouseListener* _pListener);
	const math::Vec2&				GetPos() const { return m_CurrentPos; };
	math::Vec2						GetImmediatePos();

	BUTTON_STATE*					GetButtonState(BUTTONS b) { return &m_ButtonA[b]; };

private:
	Mouse();
	~Mouse();
	/// find what index this listener resides at
	int								GetListenerIndex(MouseListener* _pListener);
	std::vector<MouseListener*>		m_Listeners;

	// track mouse info
	math::Vec2						m_CurrentPos, m_LastPos;
	BUTTON_STATE					m_ButtonA[BUTTON_COUNT];
	float							m_HooverThreshold;
	float							m_HooverTime;
	float							m_MouseSpeed;
	float							m_DoubleClkTime;

	bool							m_InitDragging, m_DragStarted;
	math::Vec2						m_StartDragPos;
	float							m_MinDragPos;
    int                             m_FingerID[BUTTON_COUNT];
    math::Vec2                      m_FingerPos[BUTTON_COUNT];
    BUTTONS                         m_LastFingerID;
    
};

class MouseListener
{
public:
	/// will automatically register itself with the mouse handler \n
	/// IMPORTANT that you do this!
						MouseListener();
	/// will automatically de-register itself with the keyboard handler
	virtual				~MouseListener();
	/// key was pressed this frame
	virtual	void		MouseButtonPressed(Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos) {};
	/// key was double-clicked this time. Note that this is sent as well as mouse button pressed
	virtual	void		MouseButtonDblClick(Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos) {};
	/// key was released this frame
	virtual void		MouseButtonReleased(Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos) {};
	/// key was pressed last frame && this frame. \n
	/// note that you WILL get this message at the same time as KeyPressed
	virtual void		MouseButtonDown(Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos) {};

	virtual void		MouseButtonRepeat(Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos) {};

	virtual void		MouseWheelMove(int WheelInfo, const math::Vec2& Pos) {};

	/// mouse stayed at the same point for too long
	virtual void		MouseHoover(const math::Vec2& Pos) {};
	/// mouse moved
	virtual void		MouseMove(const math::Vec2& From, const math::Vec2& To) {};

	/// started dragging
	virtual void		MouseOnDrag(const math::Vec2& From, const math::Vec2& To) {};
	/// finished dragging
	virtual void		MouseOnDrop(const math::Vec2& From, const math::Vec2& To) {};

	// mouse entering client window
	virtual void		MouseEnter(const math::Vec2& EntryPos) {};
	// mouse leaving client window
	virtual void		MouseLeave(const math::Vec2& LastValidPos) {};

	Mouse*				GetMouse() const { return m_pMouse; };
	void				Disable()	{ m_IsEnabled = false; };
	void				Enable()	{ m_IsEnabled = true; };
	bool				IsEnabled() const { return m_IsEnabled; };
private:
	bool				m_IsEnabled;
	Mouse*				m_pMouse;
};


}  //namespace input

#define GET_MOUSE() input::Mouse::Instance()

#endif // _MOUSE_H_